<?php

namespace services\statistics;

use Yii;
use services\common\ExcelService;
use common\models\base\Project;
use common\models\clinic\Clinic;
use common\models\patient\User;
use common\helpers\ArrayHelper;
use common\models\statistics\ManageReport;
use common\components\Service;
use Exception;

/**
 * Class ManagementReportService
 * @package services\statistics
 */
class ManagementReportService extends Service
{
    const OUTPATIENT_TYPE = ['advisory' => 1004, 'loss' => 1002, 'initially' => 1000, 'referral' => 1001, 'break' => 1003];

    /**
     * 获取经营报表列表数据
     *
     * @param array $params
     * @param int $page
     * @param int $pageSize
     * @return array
     * @author wangxiang
     */
    public function getData(array $params, int $page = 0, int $pageSize = 0): array
    {
        $query = ManageReport::find()
            ->where(['project_id' => $params['project_id'], 'type' => $params['type'], 'pid' => 0])
            ->andWhere(['between', 'time', $params['start_time'], $params['end_time']])
            ->orderBy(['time' => SORT_DESC]);

        // 总数
        $total = $query->count();

        // 分页
        if ($page && $pageSize) {
            $query->limit($pageSize)->offset(($page - 1) * $pageSize);
        }

        $idData = $query->select('id')->column();

        $list = ManageReport::find()
            ->where(['id' => $idData])
            ->orWhere(['pid' => $idData])
            ->orderBy(['clinic_sort' => SORT_ASC, 'time' => SORT_DESC, 'clinic_id' => SORT_DESC])
            ->asArray()
            ->all();

        foreach ($list as &$item) {
            if ($item['type'] == ManageReport::TYPE_DAY) {
                $item['time'] = date('Y-m-d', $item['time']);
            } else if ($item['type'] == ManageReport::TYPE_MONTH) {
                $item['time'] = date('Y-m', $item['time']);
            } else {
                $item['time'] = date('Y', $item['time']);
            }
            if ($item['pid'] == 0) {
                $item['clinic_name'] = '诊室汇总';
            }

            if ($item['loss_rate'] == -1) {
                $item['loss_rate'] = '--';
            } else {
                $item['loss_rate'] = ($item['loss_rate'] * 100) . '%';
            }

            $item['loss_rate'] == -1 && $item['loss_rate'] = '--';
            $item['last_month_treat_num'] == -1 && $item['last_month_treat_num'] = '--';
            if ($item['diagnosis_rate'] == -1) {
                $item['diagnosis_rate'] = '--';
            } else {
                $item['diagnosis_rate'] = ($item['diagnosis_rate'] * 100) . '%';
            }
        }

        $list = ArrayHelper::tree($list);

        return [
            'page' => (int)$page,
            'pageSize' => (int)$pageSize,
            'total' => (int)$total,
            'list' => $list,
        ];
    }


    /**
     * 生成快照
     *
     * @param int $time
     * @param int $projectId
     * @throws Exception
     * @author wangxiang
     */
    public function generateSnapshot(int $time, int $projectId = 0): void
    {
        $projectIdData = [$projectId];
        if (!$projectId) {
            $projectIdData = Project::find()->where(['status' => Project::STATUS_ENABLE])->select('id')->column();
        }

        // 获取诊室信息
        $clinicArr = Clinic::find()
            ->where(['status' => Clinic::STATUS_ENABLE])
            ->select(['id', 'name', 'sort', 'project_id'])
            ->orderBy('id')
            ->asArray()
            ->all();

        $clinicData = [];
        foreach ($clinicArr as $tempClinicArr) {
            $clinicData[$tempClinicArr['project_id']][] = $tempClinicArr;
        }
        // 获取上个月快照数据
        $lastMonth = strtotime(date('Y-m-01', strtotime("-1 month", $time)));
        $monthManagementReportData = ManageReport::find()
            ->where(['type' => ManageReport::TYPE_MONTH, 'time' => $lastMonth])
            ->select(['project_id', 'clinic_id', 'treat_num'])
            ->asArray()
            ->all();
        $monthManagementReportProData = [];
        foreach ($monthManagementReportData as $tempArr) {
            $monthManagementReportProData[$tempArr['project_id']][$tempArr['clinic_id']] = $tempArr['treat_num'];
        }

        foreach ($projectIdData as $projectId) {

            if (!isset($clinicData[$projectId])) {
                continue;
            }
            $lastTreatData = $monthManagementReportProData[$projectId] ?? [];

            $transaction = Yii::$app->db->beginTransaction();
            try {
                // 判断时间是不是该月最后一天， 是不是该年最后一天
                if (date('d', $time) == date('t', $time)) {
                    // 是本月的最后一天， 生成一个月数据快照
                    $startTime = strtotime(date('Y-m-01', $time));
                    $endTime = strtotime(date('Y-m-d', $startTime) . '+1 month') - 1;
                    $this->_createData($projectId, $startTime, $endTime, $clinicData[$projectId], $lastTreatData, ManageReport::TYPE_MONTH);

                    if (date('m', $time) == 12) {
                        // 是本年的最后一天， 生成一个年数据快照
                        $startTime = strtotime(date('Y-01-01', $time));
                        $endTime = strtotime(date('Y-m-d', $startTime) . '+1 year') - 1;
                        $this->_createData($projectId, $startTime, $endTime, $clinicData[$projectId], $lastTreatData, ManageReport::TYPE_YEAR);
                    }
                }

                // 生成一个日数据快照
                $startTime = strtotime(date('Y-m-d', $time));
                $endTime = strtotime(date('Y-m-d', $startTime) . '+1 day') - 1;
                $this->_createData($projectId, $startTime, $endTime, $clinicData[$projectId], $lastTreatData, ManageReport::TYPE_DAY);

                $transaction->commit();
            } catch (Exception $exception) {
                $transaction->rollBack();
                throw new Exception('项目id：' . $projectId . '--错误行：' . $exception->getLine() . '--错误信息：' . $exception->getMessage(), 500);
            }

            //echo $projectId . PHP_EOL;

        }

    }

    /**
     * 处理快照数据入库
     *
     * @param int $projectId
     * @param string $startTime
     * @param string $endTime
     * @param array $clinicData
     * @param $lastTreatData
     * @param int $type
     * @throws \yii\db\Exception
     * @author wangxiang
     */
    private function _createData(int $projectId, string $startTime, string $endTime, array $clinicData, $lastTreatData, int $type = ManageReport::TYPE_DAY): void
    {
        // 判断快照是否已生成
        $flag = ManageReport::find()->where(['project_id' => $projectId, 'type' => $type, 'time' => $startTime, 'pid' => 0])->exists();
        if (!$flag) {
            $patientData = User::find()
                ->where([
                    'or',
                    ['between', 'reg_time', $startTime, $endTime],
                    ['between', 'last_revisiting_time', $startTime, $endTime],
                ])
                ->andWhere(['status' => User::STATUS_ENABLE, 'project_id' => $projectId])
                ->select(['id', 'clinic_id', 'outpatient_type_id', 'reg_time', 'last_revisiting_time', 'is_introduce', 'revisiting_count'])
                ->orderBy('clinic_id')
                ->asArray()
                ->all();

            $insertData = [];

            // 先存一条汇总数据， 是为了获取自增id
            $model = new ManageReport;
            $model->project_id = $projectId;
            $model->time = $startTime;
            $model->type = $type;
            if (!$model->save()) {
                throw new Exception(json_encode($model->errors), 422);
            }

            foreach ($clinicData as $clinicItem) {
                $temp = [
                    'project_id' => $projectId,
                    'pid' => $model->id,
                    'time' => $startTime,
                    'type' => $type,
                    'clinic_id' => $clinicItem['id'],
                    'clinic_name' => $clinicItem['name'],
                    'clinic_sort' => $clinicItem['sort'],
                    'reg_num' => 0,
                    'advisory_num' => 0,
                    'loss_num' => 0,
                    'loss_rate' => -1,
                    'initially_num' => 0,
                    'referral_num' => 0,
                    'treat_num' => 0,
                    'last_month_treat_num' => -1,
                    'diagnosis_rate' => -1,
                    'introduction_num' => 0,
                    'created_at' => time(),
                    'updated_at' => time(),
                ];

                foreach ($patientData as $key => $patientItem) {

                    if ($patientItem['clinic_id'] == $clinicItem['id']) {

                        // 总挂号
                        if ($patientItem['reg_time'] >= $startTime && $patientItem['reg_time'] <= $endTime) {
                            $temp['reg_num']++;
                        }

                        // 咨询号
                        if ($patientItem['reg_time'] >= $startTime && $patientItem['reg_time'] <= $endTime && $patientItem['outpatient_type_id'] == self::OUTPATIENT_TYPE['advisory'] && $patientItem['revisiting_count'] == 1) {
                            $temp['advisory_num']++;
                        }

                        // 流失
                        if ($patientItem['reg_time'] >= $startTime && $patientItem['reg_time'] <= $endTime && $patientItem['outpatient_type_id'] == self::OUTPATIENT_TYPE['loss']) {
                            $temp['loss_num']++;
                        }

                        // 初诊
                        if ($patientItem['last_revisiting_time'] >= $startTime && $patientItem['last_revisiting_time'] <= $endTime && $patientItem['outpatient_type_id'] == self::OUTPATIENT_TYPE['initially']) {
                            $temp['initially_num']++;
                        }

                        // 复诊
                        if (($patientItem['last_revisiting_time'] >= $startTime && $patientItem['last_revisiting_time'] <= $endTime) && ($patientItem['outpatient_type_id'] == self::OUTPATIENT_TYPE['referral'] || ($patientItem['outpatient_type_id'] == self::OUTPATIENT_TYPE['advisory'] && $patientItem['revisiting_count'] > 1))) {
                            $temp['referral_num']++;
                        }

                        // 介绍号
                        if ($patientItem['last_revisiting_time'] >= $startTime && $patientItem['last_revisiting_time'] <= $endTime && $patientItem['is_introduce'] == User::IS_INTRODUCE_YES) {
                            $temp['introduction_num']++;
                        }

                        unset($patientData[$key]);
                    }
                }

                // 流失率
                $tempVal = $temp['reg_num'] - $temp['advisory_num'];
                $tempVal > 0 && $temp['loss_rate'] = number_format($temp['loss_num'] / $tempVal, 3);

                // 就诊数
                $temp['treat_num'] = $temp['initially_num'] + $temp['referral_num'];

                // 上月就诊数
                if (in_array($type, [ManageReport::TYPE_DAY, ManageReport::TYPE_MONTH])) {
                    isset($lastTreatData[$clinicItem['id']]) && $temp['last_month_treat_num'] = $lastTreatData[$clinicItem['id']];
                }

                // 断诊率
                $temp['last_month_treat_num'] > 0 && $temp['diagnosis_rate'] = (1 - number_format($temp['referral_num'] / $temp['last_month_treat_num'], 3));

                $insertData[] = $temp;

                // 汇总数据统计
                $model->reg_num += $temp['reg_num'];
                $model->advisory_num += $temp['advisory_num'];
                $model->loss_num += $temp['loss_num'];
                $model->initially_num += $temp['initially_num'];
                $model->referral_num += $temp['referral_num'];
                $model->introduction_num += $temp['introduction_num'];
            }

            // 汇总流失率
            $tempAllVal = $model->reg_num - $model->advisory_num;
            $tempAllVal > 0 && $model->loss_rate = number_format($model->loss_num / $tempAllVal, 3);

            // 汇总就诊数
            $model->treat_num = $model->initially_num + $model->referral_num;

            // 汇总上月就诊数
            if (in_array($type, [ManageReport::TYPE_DAY, ManageReport::TYPE_MONTH])) {
                isset($lastTreatData[0]) && $model->last_month_treat_num = $lastTreatData[0];
            }

            // 汇总断诊率
            $model->last_month_treat_num > 0 && $model->diagnosis_rate = (1 - number_format($model->referral_num / $model->last_month_treat_num, 3));

            if (!$model->save()) {
                throw new Exception(json_encode($model->errors), 422);
            }

            Yii::$app->db->createCommand()->batchInsert(ManageReport::tableName(), [
                'project_id', 'pid', 'time', 'type', 'clinic_id', 'clinic_name', 'reg_num', 'advisory_num', 'loss_num', 'loss_rate', 'initially_num',
                'referral_num', 'treat_num', 'last_month_treat_num', 'diagnosis_rate', 'introduction_num', 'created_at', 'updated_at'
            ], $insertData)->execute();
        }
    }


    /**
     * 钉钉机器人提醒
     *
     * @param string $webHook
     * @param string $errorMsg
     * @author wangxiang
     */
    public function dingMsg(string $webHook, string $errorMsg = ''): void
    {
        $templateText = "### **报表-经营-经营报表快照生成错误提醒**" . PHP_EOL;
        $templateText .= "### " . date('Y-m-d H:i:s') . PHP_EOL;
        $templateText .= "### " . '脚本地址： management-report/index' . PHP_EOL;
        $templateText .= "### " . $errorMsg . PHP_EOL;

        $data['msgtype'] = 'markdown';
        $data['markdown'] = [
            'title' => '**报表-经营-经营报表快照生成错误提醒**',
            'text' => $templateText
        ];
        $data['at'] = [
            'atMobiles' => [],
            'isAtAll' => false
        ];

        $msgStr = json_encode($data);
        ExcelService::request_by_curl($webHook, $msgStr);
    }


    /**
     * 修复经营报表快照挂号数据
     *
     * @param int $projectId
     * @param int $type 维度 1：日， 2：月， 3：年
     * @param int $time
     * @author wangxiang
     */
    public function repairSnapshot(int $projectId, int $type, int $time)
    {
        $data = ManageReport::find()
            ->where([
                'time' => $time,
                'type' => $type,
                'project_id' => $projectId
            ])
            ->select(['id', 'clinic_id'])
            ->asArray()
            ->all();

        if ($data) {
            if ($type == ManageReport::TYPE_DAY) {
                $startTime = strtotime(date('Y-m-d', $time));
                $endTime = strtotime(date('Y-m-d', $startTime) . '+1 day') - 1;
            } else if ($type == ManageReport::TYPE_MONTH) {
                $startTime = strtotime(date('Y-m-01', $time));
                $endTime = strtotime(date('Y-m-d', $startTime) . '+1 month') - 1;
            } else if ($type == ManageReport::TYPE_YEAR) {
                $startTime = strtotime(date('Y-01-01', $time));
                $endTime = strtotime(date('Y-m-d', $startTime) . '+1 year') - 1;
            } else {
                return;
            }

            $tempArr = [];

            $patientData = User::find()
                ->where(['between', 'reg_time', $startTime, $endTime])
                ->andWhere(['status' => User::STATUS_ENABLE, 'project_id' => $projectId])
                ->select(['clinic_id', 'count(clinic_id) as num'])
                ->groupBy('clinic_id')
                ->asArray()
                ->all();

            foreach ($patientData as $arr) {
                $tempArr[$arr['clinic_id']] = $arr['num'];
                $tempArr[0] = isset($tempArr[0]) ? $tempArr[0] + $arr['num'] : $arr['num'];
            }

            foreach ($data as $item) {
                $model = ManageReport::findOne($item['id']);
                if (!$model || !isset($tempArr[$item['clinic_id']])) continue;
                $model->reg_num = $tempArr[$item['clinic_id']];
                $model->save();
            }
        }
    }


}
